package org.zoomdev.zoom.aop.modules;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.zoomdev.zoom.aop.Invoker;
import org.zoomdev.zoom.aop.factory.impl.AsmClassloader;
import org.zoomdev.zoom.aop.utils.AsmUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import static org.objectweb.asm.Opcodes.*;


/**
 * 核心使用javassist作为类字节码工具
 * <p>
 * 不对外开放，假如要测试，使用IocBuilder
 *
 * @author jzoom
 */
public class AsmAopFactory extends AbstractMethodInterceptorFactory {


    public static final String INVOKER_FIELD_NAME = "_$invokers";
    public static final String PRIVATE_METHOD_NAME = "_$methods";
    private static final String INVOKER_FIELD_PREFIX = "_$invokers";


    public AsmAopFactory() {
        super(AsmClassloader.get());
    }

    @Override
    public ClassLoader getClassLoader() {
        return AsmClassloader.get();
    }


    private static void generateConstructor(ClassWriter cw, Class<?> superClass, Constructor<?> superConstructor) {
        // 获取构造函数描述符
        String descriptor = Type.getConstructorDescriptor(superConstructor);

        // 创建方法访问器
        MethodVisitor mv = cw.visitMethod(
                getAdjustedModifiers(superConstructor),
                "<init>",
                descriptor,
                null,
                AsmUtils.getExceptionTypes(superConstructor)
        );

        mv.visitCode();

        // 加载this指针（ALOAD 0）
        mv.visitVarInsn(ALOAD, 0);

        // 加载所有参数
        Class<?>[] paramTypes = superConstructor.getParameterTypes();
        for (int i = 0; i < paramTypes.length; i++) {
            Type type = Type.getType(paramTypes[i]);
            mv.visitVarInsn(type.getOpcode(ILOAD), i + 1);
        }

        // 调用父类构造函数
        mv.visitMethodInsn(INVOKESPECIAL,
                Type.getInternalName(superClass),
                "<init>",
                descriptor,
                false);

        // 返回指令
        mv.visitInsn(RETURN);

        // 自动计算最大栈和局部变量表
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private static void generateDefaultConstructor(ClassWriter cw, Class<?> superClass) {
        MethodVisitor mv = cw.visitMethod(
                ACC_PUBLIC,
                "<init>",
                "()V",
                null,
                null
        );

        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL,
                Type.getInternalName(superClass),
                "<init>",
                "()V",
                false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
    }

    private static int getAdjustedModifiers(Constructor<?> constructor) {
        int modifiers = constructor.getModifiers();
        // 移除父类构造函数的 private 修饰符
        if (Modifier.isPrivate(modifiers)) {
            return modifiers & ~ACC_PRIVATE | ACC_PUBLIC;
        }
        return modifiers;
    }

    ////////////////////////////////////////////////////////////////////////////

    @Override
    protected Class<?> enhance(Class<?> src, AopConfig[] configs) throws Exception {
        String enhancedClassName = src.getName() + TAIL;
        byte[] bytecode = generateBytecode(src, enhancedClassName, configs);
        return AsmClassloader.defineClass(enhancedClassName, bytecode);
    }

    private byte[] generateBytecode(Class<?> srcClass, String enhancedClassName, AopConfig[] configs) {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);

        // 定义类基本信息
        cw.visit(AsmUtils.VERSION, ACC_PUBLIC, enhancedClassName.replace('.', '/'), null,
                Type.getInternalName(srcClass), null);

        // 生成所有构造函数
        for (Constructor<?> constructor : srcClass.getDeclaredConstructors()) {
            generateConstructor(cw, srcClass, constructor);
        }


        // 添加静态字段存储 Invoker 实例
        for (int i = 0; i < configs.length; i++) {
            addInvokerField(cw, i);
        }
//
        // 生成覆盖方法
        for (int i = 0; i < configs.length; i++) {
            AopConfig config = configs[i];
            generateEnhancedMethod(cw, srcClass, config.method);
            generateOverriddenMethod(cw, srcClass, enhancedClassName, config.method, i);
        }

        cw.visitEnd();
        return cw.toByteArray();
    }

    private void generateEnhancedMethod(ClassWriter cw, Class<?> superClass, Method method) {
        // 方法名变为 __ + 原方法名
        String newMethodName = "__" + method.getName();

        // 创建方法访问器
        MethodVisitor mv = cw.visitMethod(
                ACC_PUBLIC,
                newMethodName,
                Type.getMethodDescriptor(method),
                null,
                AsmUtils.getExceptionTypes(method)
        );

        // --- 开始生成字节码 ---
        mv.visitCode();

        // 加载this指针（ALOAD 0）
        mv.visitVarInsn(ALOAD, 0);

        // 加载所有参数到操作数栈
        Type[] paramTypes = Type.getArgumentTypes(method);
        for (int i = 0; i < paramTypes.length; i++) {
            Type paramType = paramTypes[i];
            int loadOp = paramType.getOpcode(ILOAD);
            mv.visitVarInsn(loadOp, i + 1); // 参数索引从1开始
        }

        // 调用父类方法（方法生产核心）
        mv.visitMethodInsn(
                INVOKESPECIAL,
                Type.getInternalName(superClass),   // 父类类型
                method.getName(),                   // 原方法名
                Type.getMethodDescriptor(method),   // 方法描述符
                false                               // 非接口调用
        );

        // 处理返回值
        Type returnType = Type.getReturnType(method);
        if (returnType == Type.VOID_TYPE) {
            mv.visitInsn(RETURN);
        } else {
            mv.visitInsn(returnType.getOpcode(IRETURN));
        }

        // 自动计算栈和局部变量大小
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void addInvokerField(ClassWriter cw, int index) {
        FieldVisitor fv = cw.visitField(
                ACC_PROTECTED | ACC_STATIC,
                INVOKER_FIELD_PREFIX + index,
                Type.getDescriptor(Invoker.class),
                null, null);
        fv.visitEnd();
    }

    private void generateOverriddenMethod(ClassWriter cw, Class<?> srcClass, String enhancedClassName, Method method, int index) {
        String methodName = method.getName();
        String descriptor = Type.getMethodDescriptor(method);
        MethodVisitor mv = cw.visitMethod(
                ACC_PUBLIC,
                methodName,
                descriptor,
                null,
                getExceptionTypes(method));

        mv.visitCode();


        // 生成增强类名（假设 enhancedClassName 是类似 "com.example.MyClass_Enhanced"）
        String enhancedInternalName = enhancedClassName.replace('.', '/');

        // 加载 Invoker 静态字段
        mv.visitFieldInsn(GETSTATIC,
                enhancedInternalName,
                INVOKER_FIELD_PREFIX + index,
                Type.getDescriptor(Invoker.class));

        // 加载 this 指针
        mv.visitVarInsn(ALOAD, 0);

        // 创建参数数组
        generateParameterArray(method, mv);

        // 调用 Invoker.invoke()
        mv.visitMethodInsn(INVOKEINTERFACE,
                Type.getInternalName(Invoker.class),
                "invoke",
                "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
                true);

        // 处理返回值
        handleReturnType(method.getReturnType(), mv);

        mv.visitMaxs(0, 0); // 自动计算
        mv.visitEnd();
    }

    private void generateParameterArray(Method method, MethodVisitor mv) {
        int paramCount = method.getParameterTypes().length;
        mv.visitIntInsn(Opcodes.BIPUSH, paramCount);
        mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");

        for (int i = 0; i < paramCount; i++) {
            mv.visitInsn(Opcodes.DUP);
            mv.visitIntInsn(Opcodes.BIPUSH, i);
            loadParameter(mv, method.getParameterTypes()[i], i + 1);
            AsmUtils.boxIfPrimitive(mv, method.getParameterTypes()[i]);
            mv.visitInsn(Opcodes.AASTORE);
        }
    }

    private void loadParameter(MethodVisitor mv, Class<?> paramType, int index) {
        int opcode = Type.getType(paramType).getOpcode(Opcodes.ILOAD);
        mv.visitVarInsn(opcode, index);
    }

    private void handleReturnType(Class<?> returnType, MethodVisitor mv) {
        if (returnType == void.class) {
            mv.visitInsn(Opcodes.POP);
            mv.visitInsn(Opcodes.RETURN);
        } else {
            AsmUtils.unboxIfPrimitive(mv, returnType);
            mv.visitInsn(Type.getType(returnType).getOpcode(Opcodes.IRETURN));
        }
    }

    private String[] getExceptionTypes(Method method) {
        Class<?>[] exceptions = method.getExceptionTypes();
        String[] exceptionInternalNames = new String[exceptions.length];
        for (int i = 0; i < exceptions.length; i++) {
            exceptionInternalNames[i] = Type.getInternalName(exceptions[i]);
        }
        return exceptionInternalNames;
    }


}
